#!/usr/bin/python
# -*- coding:utf-8 -*-
import time
import copy
import datetime
import pickle

from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.tokens import RefreshToken

from utilities.redis import captcha_redis
from apps.global_config.serializer import get_global_config

REDIS_DB = captcha_redis


class Singleton(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            orig = super(Singleton, cls)
            cls._instance = orig.__new__(cls, *args, **kwargs)
        return cls._instance


def user_can_authenticate(user):
    is_freeze = getattr(user, "is_freeze", False)
    is_active = getattr(user, "is_active", False)
    expired_time = getattr(user, "expired_time", "")
    if (
        isinstance(expired_time, datetime.datetime)
        and expired_time < datetime.datetime.now()
    ):
        return False
    return is_freeze is False and is_active is True


def get_page_args(request):
    query_data = request.query_params
    try:
        page = int(query_data.get("page", 1))
        page_size = int(query_data.get("pageSize", 10))
        if any([page < 1, page_size < 1]):
            raise ValueError
    except (ValueError, TypeError):
        page, page_size = 1, 10
    return page, page_size


def is_login_limit(username, obj_global_config):
    """登录错误次数限制，按小时计数，达到次数限制，限制登录一定时间"""
    key = f'{username}_login_fail'
    try:
        count = int(REDIS_DB.get(key))
    except Exception:
        count = 1
    REDIS_DB.set(key, count + 1)
    # if count > LOGIN_FAIL_LIMIT_COUNT:
    #     REDIS_DB.expire(key, LOGIN_FAIL_LIMIT_TIME)
    #     return True, LOGIN_FAIL_LIMIT_COUNT - count
    # if count == LOGIN_FAIL_LIMIT_COUNT:
    #     REDIS_DB.expire(key, LOGIN_FAIL_LIMIT_TIME)
    # return False, LOGIN_FAIL_LIMIT_COUNT - count

    max_retry_count = obj_global_config.max_retry_count
    limit_time = obj_global_config.locked_time * 60  # trans min to sec
    if count > max_retry_count:
        REDIS_DB.expire(key, limit_time)
        return True, max_retry_count - count
    if count == max_retry_count:
        REDIS_DB.expire(key, limit_time)
    return False, max_retry_count - count


def cut_login_counter(username, count=1):
    """减少用户登录错误记录次数"""
    key = f'{username}_login_fail'
    try:
        redis_count = int(REDIS_DB.get(key))
    except Exception:
        return
    ex_time = REDIS_DB.ttl(key)
    if redis_count and ex_time == -1:
        REDIS_DB.set(key, redis_count - count)
    elif redis_count and ex_time != -1:
        REDIS_DB.set(key, redis_count - count, ex=ex_time)


def clear_login_counter(username):
    """清空登录计数器"""
    key = f'{username}_login_fail'
    REDIS_DB.delete(key)


def get_header_token(request, only_token=False):
    auth = JWTAuthentication()
    try:
        header = auth.get_header(request)
        if header is None:
            return None
        raw_token = auth.get_raw_token(header)
        if raw_token is None:
            return None
        if only_token:
            return raw_token
        validated_token = auth.get_validated_token(raw_token)
        auth.get_user(validated_token)
    except Exception:
        return None
    else:
        exp = int(validated_token['exp']) - int(time.time())
        return str(raw_token), exp


def get_refresh_token_exp(refresh):
    if not refresh:
        return None
    if REDIS_DB.get(f"websoc_black_refresh_{refresh}"):
        return None
    try:
        rt = RefreshToken(refresh)
    except Exception:
        return None
    return int(rt['exp']) - int(time.time())


def cache_user_token(access_token=None, refresh_token=None):
    """根据用户设置缓存token"""
    global_config = get_global_config()
    access_token_limit = global_config.access_token_limit * 60
    if access_token:
        REDIS_DB.set(f"wass_access_token_{access_token}", refresh_token, ex=access_token_limit)
    if refresh_token:
        REDIS_DB.set(f"wass_refresh_token_{refresh_token}", 1, ex=access_token_limit)


def check_token_expire(token, token_type="access"):
    """用户登录过期实际是refresh token 过期了"""
    token = token.decode() if isinstance(token, bytes) else token
    cache_value = REDIS_DB.get(f"wass_{token_type}_token_{token}")
    if token_type != "access" and cache_value:
        return True
    elif token_type != "access" and not cache_value:
        return False
    elif token_type == "access" and not cache_value:
        return False
    elif token_type == "access" and cache_value:
        refresh = cache_value.decode() if isinstance(cache_value, bytes) else cache_value
        if REDIS_DB.get(f"wass_refresh_token_{refresh}"):
            return True
    return False


def delete_token(token, token_type="access"):
    REDIS_DB.delete(f"wass_{token_type}_token_{token}")


def cache_login_count_data(user_id, refresh_token):
    redis_data = REDIS_DB.get("wass_login_count_data")
    if redis_data:
        exists_login_data = pickle.loads(redis_data)
    else:
        exists_login_data = {}
    this_user_data = exists_login_data.get(user_id, [])
    not_used_refresh = []
    for refresh in this_user_data:
        if not REDIS_DB.get(f"wass_refresh_token_{refresh}"):
            not_used_refresh.append(refresh)
    for refresh in not_used_refresh:
        this_user_data.remove(refresh)
    global_config = get_global_config()
    if len(this_user_data) >= global_config.max_multiple_devices:
        return False, f"管理员设置了同一账号同时最多只能有{global_config.max_multiple_devices}个终端登录"
    if refresh_token:
        this_user_data.append(refresh_token)
    exists_login_data[user_id] = this_user_data
    REDIS_DB.set("wass_login_count_data", pickle.dumps(exists_login_data))
    return True, "SUCCESS"


def clear_cache_token():
    access_token = [a.decode() for a in REDIS_DB.keys("wass_access_token_*")]
    refresh_token = [r.decode() for r in REDIS_DB.keys("wass_refresh_token_*")]
    refresh_token.extend(access_token)
    REDIS_DB.delete(*refresh_token)


def get_user_account_log(origin_user, changed_info: dict):
    """
    OrderedDict([('real_name', '彭婉悦'),
    ('email', 'pengwy@knownsec.com'),
    ('phone', ''),
    ('department', ''),
    ('group', [审计员]),
    ('expired_time', datetime.datetime(2022, 7, 31, 7, 5, 40, 440000, tzinfo=<UTC>)),
    ('limit_targets', [])])
    """
    file_maps = {
        'real_name': "真实姓名",
        'email': "电子邮箱",
        'phone': "电话号码",
        'department': "所在部门",
        'group': "权限角色",
        'expired_time': "用户有效期",
        'limit_targets': "限制目标范围",
    }
    result = dict()
    new_user_info = copy.deepcopy(changed_info)
    for k, v in new_user_info.items():
        if k in origin_user:
            if k == 'expired_time':
                try:
                    expired_time = v.strftime('%Y-%m-%d %H:%M:%S')
                    if origin_user[k].strftime('%Y-%m-%d %H:%M:%S') != expired_time:
                        result.update({k: expired_time})
                except Exception:
                    result.update({k: v})
                continue
            if origin_user[k] != v:
                result.update({k: v})
        else:
            result.update({k: v})

    detail = ""
    for k, v in result.items():
        if k == 'group':
            v = v[0]
        if k == 'limit_targets':
            v = ','.join(v)
        detail += ", 修改{}为: {}".format(file_maps.get(k, '其他'), v)
    detail = detail.strip(',')
    return detail
